﻿namespace EasyToolkit.Excel.ContextualMenus
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;
    using EasyToolkit.Excel.BindingTemplates;
    using EasyToolkit.Excel.BindingTemplates.Definitions;
    using EasyToolkit.Excel.ContextualMenus.Definition;
    using EasyToolkit.Tools.Extensions;
    using Microsoft.Office.Interop.Excel;

    public delegate void MenuAction(Range range);

    static class ContextualMenuFactory
    {
        static public IContextualMenu CreateInstance(Range range, string contextMenuPath, Type mainBindingDefinitionType)
        {
            IContextualMenu menu = null;

            if (!string.IsNullOrEmpty(contextMenuPath))
            {
                string menuName = null;
                Worksheet sheetContainer = null;
                try
                {
                    string[] contextMenuParts = contextMenuPath.Split('.');
                    Worksheet currentWorkSheet = range.Worksheet;
                    if (contextMenuParts.Count() == 1)
                    {
                        sheetContainer = range.Worksheet;
                        menuName = contextMenuParts[0].EmptyIfNull().Trim();
                    }
                    else
                    {
                        string worksheetContainerName = contextMenuParts[0].EmptyIfNull().Trim();
                        menuName = contextMenuParts[1].EmptyIfNull().Trim();

                        Workbook workbook = currentWorkSheet.Parent as Workbook;
                        if (workbook == null)
                            throw new EasyToolkitException("Cannot retrieve the sheet workbook");

                        List<Worksheet> sheets = new List<Worksheet>(workbook.Worksheets.Cast<Worksheet>());
                        sheetContainer = sheets.FirstOrDefault(s => !string.IsNullOrEmpty(s.Name) && s.Name.Equals(worksheetContainerName));
                        if (sheetContainer == null)
                            throw new EasyToolkitException(string.Format("Cannot find the sheet '{0}' in the current workbook", worksheetContainerName), false);

                        Marshal.ReleaseComObject(workbook);
                        workbook = null;
                    }

                    Range menuRange = sheetContainer.Cells.Find(string.Format(ExcelTemplateDefinition.CONTEXTUALMENU_START_FORMAT, menuName), Type.Missing, XlFindLookIn.xlValues, XlLookAt.xlPart, XlSearchOrder.xlByRows, XlSearchDirection.xlNext, false);
                    if (menuRange == null)
                        throw new EasyToolkitException("Cannot find the ContextualMenu");

                    Marshal.ReleaseComObject(sheetContainer);
                    sheetContainer = null;
                    Marshal.ReleaseComObject(currentWorkSheet);
                    currentWorkSheet = null;

                    string xmlValue = menuRange.Value2;
                    XmlContextualMenuDefinition xmlDefinition = XmlContextualMenuDefinition.CreateInstance(xmlValue);
                    menu = ContextualMenuFactory.CreateInstance(menuRange, xmlDefinition, mainBindingDefinitionType);
                }
                catch (Exception ex)
                {
                    throw new EasyToolkitException(string.Format("Cannot create ContextualMenu '{0}-{1}'", menuName.EmptyIfNull(), sheetContainer == null ? string.Empty : sheetContainer.Name.EmptyIfNull()), ex);
                }
            }
            return menu;
        }

        static private IContextualMenu CreateInstance(Range range, XmlContextualMenuDefinition definition, Type mainBindingDefinitionType)
        {
            ContextualMenu ret = null;
            if (definition != null)
            {
                definition.Name = definition.Name.EmptyIfNull().Trim();
                if (string.IsNullOrEmpty(definition.Name))
                    throw new EasyToolkitException("Contextual menu must have a name");

                if (definition.Items != null && definition.Items.Count > 0)
                {
                    List<IContextualPart> items = new List<IContextualPart>();
                    foreach (XmlContextualMenuPart xmlPart in definition.Items)
                    {
                        if (xmlPart is XmlContextualMenuDefinition)
                        {
                            XmlContextualMenuDefinition xmlMenu = xmlPart as XmlContextualMenuDefinition;
                            IContextualMenu subMenu = null;
                            // If no item, then we consider that the menu is set elsewhere
                            if (xmlMenu.Items == null || xmlMenu.Items.Count() == 0)
                                subMenu = CreateInstance(range, xmlMenu.Name, mainBindingDefinitionType);
                            else
                                subMenu = CreateInstance(range, xmlMenu, mainBindingDefinitionType);
                            if (subMenu != null)
                                items.Add((IContextualPart) subMenu);
                        }
                        else
                        {
                            XmlContextualMenuItemDefinition xmlItem = xmlPart as XmlContextualMenuItemDefinition;
                            MethodInfo methodInfo = ConstextualMethodRetriever.RetrieveContextualMethodInfo(mainBindingDefinitionType, xmlItem.Action);
                            IContextualMenuItem menuItem = new ContextualMenuItem(xmlItem.Caption, xmlItem.BeginGroup, methodInfo, xmlItem.FaceId);
                            items.Add((IContextualPart) menuItem);
                        }
                    }
                    if(items.Count > 0)
                        ret = new ContextualMenu(definition.Name, definition.Caption, definition.BeginGroup, items);
                }
            }
            return ret;
        }
    }
}
